using System;
using System.Collections.Generic;
using System.Linq;
using Castle.DynamicProxy;
using Microsoft.Practices.ObjectBuilder2;

namespace UniAspect.Aspect
{
    public class ProxyFactoryPolicy : IProxyFactoryPolicy
    {
        private readonly IList<IAspect> _interceptors = new List<IAspect>();
        private readonly Type _originalType;
        private readonly ProxyGenerator _generator = new ProxyGenerator();
        private readonly IBuildPlanCreatorPolicy _constructorBuildPolicy;

        public ProxyFactoryPolicy(Type originalType, IBuildPlanCreatorPolicy constructorBuildPolicy)
            : this(constructorBuildPolicy)
        {
            _originalType = originalType;
        }

        public ProxyFactoryPolicy(IBuildPlanCreatorPolicy constructorBuildPolicy)
        {
            _constructorBuildPolicy = constructorBuildPolicy;
        }

        public IList<IAspect> Interceptors
        {
            get { return _interceptors; }
        }

        public void BuildUp(IBuilderContext context)
        {
            if(context.Existing != null)
                return;

            Type typeToBuild = BuildKey.GetType(context.BuildKey);
            IBuildPlanPolicy plan = _constructorBuildPolicy.CreatePlan(context, context.BuildKey);
            context.Policies.Set(plan, context.BuildKey);

            if(_originalType == null)
            {
                context.Existing = _generator.CreateClassProxy(typeToBuild, _interceptors.ToArray());
                //original build plan is compulsory 'cuz it resolves dependencies
                plan.BuildUp(context);
            }
            else
            {
                //original build plan creates an underlying instance
                plan.BuildUp(context);

                //adding proxy layer between the mapping type and the instance
                context.Existing = _generator.CreateInterfaceProxyWithTarget(
                        _originalType, context.Existing, _interceptors.ToArray());
            }
        }
    }
}